Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ

Node / meshtastic / Meshtastic-Android / files / core / data / src / jvmTest / kotlin / org / meshtastic / core / data / repository / DeviceHardwareRepositoryImplTest.kt

Displaying Raw β€’ Download

core/data/src/jvmTest/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImplTest.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 17.74 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.data.repository

Tff7b72import T7ee787kotlinx.coroutines.CompletableDeferred
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.async
Tff7b72import T7ee787kotlinx.coroutines.awaitAll
Tff7b72import T7ee787kotlinx.coroutines.coroutineScope
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.flowOf
Tff7b72import T7ee787kotlinx.coroutines.runBlocking
Tff7b72import T7ee787kotlinx.serialization.json.Json
Tff7b72import T7ee787okio.Buffer
Tff7b72import T7ee787okio.Source
Tff7b72import T7ee787org.meshtastic.core.common.util.safeCatching
Tff7b72import T7ee787org.meshtastic.core.data.datasource.BundledAssetReader
Tff7b72import T7ee787org.meshtastic.core.data.datasource.DeviceHardwareLocalDataSource
Tff7b72import T7ee787org.meshtastic.core.data.datasource.decode
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.BootloaderOtaQuirksResponse
Tff7b72import T7ee787org.meshtastic.core.model.DeviceLink
Tff7b72import T7ee787org.meshtastic.core.model.EventFirmwareResponse
Tff7b72import T7ee787org.meshtastic.core.model.FirmwareReleaseManifest
Tff7b72import T7ee787org.meshtastic.core.model.MaintenanceUf2Manifest
Tff7b72import T7ee787org.meshtastic.core.model.NetworkDeviceHardware
Tff7b72import T7ee787org.meshtastic.core.model.NetworkDeviceLinksResponse
Tff7b72import T7ee787org.meshtastic.core.model.NetworkFirmwareNightly
Tff7b72import T7ee787org.meshtastic.core.model.NetworkFirmwareReleases
Tff7b72import T7ee787org.meshtastic.core.model.SoftDeviceVariant
Tff7b72import T7ee787org.meshtastic.core.network.DeviceHardwareRemoteDataSource
Tff7b72import T7ee787org.meshtastic.core.network.service.ApiService
Tff7b72import T7ee787org.meshtastic.core.repository.BootloaderOtaQuirksRepository
Tff7b72import T7ee787org.meshtastic.core.repository.DeviceLinkRepository
Tff7b72import T7ee787org.meshtastic.core.testing.FakeDatabaseProvider
Tff7b72import T7ee787kotlin.test.AfterTest
Tff7b72import T7ee787kotlin.test.BeforeTest
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertNotNull
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertTrue

Tff7b72class T56d364DeviceHardwareRepositoryImplTest Tb4b4b4{
Tff7b72private Tff7b72class T56d364FakeApiServiceTb4b4b4(Tff7b72var Te6edf3responseTb4b4b4: Te6edf3ListTff7b72<Te6edf3NetworkDeviceHardwareTff7b72>Tb4b4b4) Tb4b4b4: Te6edf3ApiService Tb4b4b4{
Tff7b72var Te6edf3hardwareCalls Tff7b72= T79c0ff0
Tff7b72var Te6edf3responseGateTb4b4b4: Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tff7b72? Tff7b72= Tff7b72null

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetDeviceHardwareTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3NetworkDeviceHardwareTff7b72> Tb4b4b4{
Te6edf3hardwareCalls Tff7b72+Tff7b72= T79c0ff1
Te6edf3responseGateTff7b72?.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3response
Tb4b4b4}

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetDeviceLinksTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3NetworkDeviceLinksResponse Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetFirmwareReleasesTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3NetworkFirmwareReleases Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetFirmwareReleaseManifestTb4b4b4(Te6edf3manifestUrlTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3FirmwareReleaseManifest Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetNightlyFirmwareTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3NetworkFirmwareNightly? Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetEventFirmwareTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3EventFirmwareResponse Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetBootloaderOtaQuirksTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3BootloaderOtaQuirksResponse Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetMaintenanceUf2ManifestTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3MaintenanceUf2Manifest Tff7b72= Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffunusedTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72class T56d364FakeBundledAssetReaderTb4b4b4(Tff7b72var Te6edf3hardwareTb4b4b4: Te6edf3ListTff7b72<Te6edf3NetworkDeviceHardwareTff7b72>Tb4b4b4, Tff7b72private Tff7b72val Te6edf3jsonTb4b4b4: Te6edf3JsonTb4b4b4) Tb4b4b4:
Te6edf3BundledAssetReader Tb4b4b4{
T8b949e/** Raw `device_bootloader_ota_quirks.json` body, or null to model the asset being absent entirely. */
Tff7b72var Te6edf3quirksJsonTb4b4b4: Tffa657String? Tff7b72= Tff7b72null

Tff7b72override Tff7b72fun Td2a8ffopenTb4b4b4(Te6edf3nameTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3Source? Tff7b72= Tff7b72when Tb4b4b4(Te6edf3nameTb4b4b4) Tb4b4b4{
Ta5d6ff"Ta5d6ffdevice_hardware.jsonTa5d6ff" Tff7b72-Tff7b72> Te6edf3BufferTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3writeTb4b4b4(Te6edf3jsonTb4b4b4.Te6edf3encodeToStringTb4b4b4(Te6edf3hardwareTb4b4b4)Tb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4)
Ta5d6ff"Ta5d6ffdevice_bootloader_ota_quirks.jsonTa5d6ff" Tff7b72-Tff7b72> Te6edf3quirksJsonTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3BufferTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3writeTb4b4b4(Tffa657itTb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tff7b72else Tff7b72-Tff7b72> Tff7b72null
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72class T56d364FakeDeviceLinkRepository Tb4b4b4: Te6edf3DeviceLinkRepository Tb4b4b4{
Tff7b72var Te6edf3reconcileCalls Tff7b72= T79c0ff0
Tff7b72var Te6edf3reconcileFailureTb4b4b4: Te6edf3Throwable? Tff7b72= Tff7b72null

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffensureImportedTb4b4b4(Tb4b4b4) Tff7b72= Tffa657Unit

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreconcileTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3reconcileCalls Tff7b72+Tff7b72= T79c0ff1
Te6edf3reconcileFailureTff7b72?.Te6edf3let Tb4b4b4{ Tff7b72throw Tffa657it Tb4b4b4}
Tb4b4b4}

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetLinksForTargetTb4b4b4(Te6edf3platformioTargetTb4b4b4: Tffa657StringTb4b4b4, Te6edf3regionCodeTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceLinkTff7b72> Tff7b72=
Te6edf3emptyListTb4b4b4(Tb4b4b4)

Tff7b72override Tff7b72fun Td2a8ffobserveAllLinksTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3ListTff7b72<Te6edf3DeviceLinkTff7b72>Tff7b72> Tff7b72= Te6edf3flowOfTb4b4b4(Te6edf3emptyListTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

T8b949e/**
* Mirrors the pre-migration `loadQuirksAsset()` behavior exactly: reads straight from the same bundled-asset fake
* the hardware-catalog seed uses, fails open to an empty response on an absent or malformed asset. Real caching and
* network-refresh behavior is covered separately in BootloaderOtaQuirksRepositoryImplTest; this fake exists so
* these tests can keep driving SoftDevice/quirk resolution through [FakeBundledAssetReader.quirksJson] unchanged.
*/
Tff7b72private Tff7b72class T56d364FakeBootloaderOtaQuirksRepositoryTb4b4b4(
Tff7b72private Tff7b72val Te6edf3assetReaderTb4b4b4: Te6edf3BundledAssetReaderTb4b4b4,
Tff7b72private Tff7b72val Te6edf3jsonTb4b4b4: Te6edf3JsonTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3BootloaderOtaQuirksRepository Tb4b4b4{
Tff7b72var Te6edf3reconcileCalls Tff7b72= T79c0ff0

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetSnapshotTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3BootloaderOtaQuirksResponse Tff7b72=
Te6edf3safeCatching Tb4b4b4{ Te6edf3assetReaderTb4b4b4.Te6edf3decodeTff7b72<Te6edf3BootloaderOtaQuirksResponseTff7b72>Tb4b4b4(Ta5d6ff"Ta5d6ffdevice_bootloader_ota_quirks.jsonTa5d6ff"Tb4b4b4, Te6edf3jsonTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72?: Te6edf3BootloaderOtaQuirksResponseTb4b4b4(Tb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreconcileTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3reconcileCalls Tff7b72+Tff7b72= T79c0ff1
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72val Te6edf3json Tff7b72= Te6edf3Json Tb4b4b4{ Te6edf3ignoreUnknownKeys Tff7b72= Tff7b72true Tb4b4b4}
Tff7b72private Tff7b72val Te6edf3dispatchers Tff7b72=
Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Tff7b72private Tff7b72val Te6edf3knownHardware Tff7b72=
Te6edf3NetworkDeviceHardwareTb4b4b4(
Te6edf3hwModel Tff7b72= T79c0ff1Tb4b4b4,
Te6edf3hwModelSlug Tff7b72= Ta5d6ff"Ta5d6ffKNOWNTa5d6ff"Tb4b4b4,
Te6edf3platformioTarget Tff7b72= Ta5d6ff"Ta5d6ffknownTa5d6ff"Tb4b4b4,
Te6edf3architecture Tff7b72= Ta5d6ff"Ta5d6ffesp32Ta5d6ff"Tb4b4b4,
Te6edf3activelySupported Tff7b72= Tff7b72trueTb4b4b4,
Te6edf3displayName Tff7b72= Ta5d6ff"Ta5d6ffKnownTa5d6ff"Tb4b4b4,
Te6edf3images Tff7b72= Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffknown.svgTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72val Te6edf3remoteOnlyHardware Tff7b72=
Te6edf3NetworkDeviceHardwareTb4b4b4(
Te6edf3hwModel Tff7b72= T79c0ff2Tb4b4b4,
Te6edf3hwModelSlug Tff7b72= Ta5d6ff"Ta5d6ffREMOTE_ONLYTa5d6ff"Tb4b4b4,
Te6edf3platformioTarget Tff7b72= Ta5d6ff"Ta5d6ffremote-onlyTa5d6ff"Tb4b4b4,
Te6edf3architecture Tff7b72= Ta5d6ff"Ta5d6ffesp32Ta5d6ff"Tb4b4b4,
Te6edf3activelySupported Tff7b72= Tff7b72trueTb4b4b4,
Te6edf3displayName Tff7b72= Ta5d6ff"Ta5d6ffRemote OnlyTa5d6ff"Tb4b4b4,
Te6edf3images Tff7b72= Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffremote-only.svgTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3databaseProviderTb4b4b4: Te6edf3FakeDatabaseProvider
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3apiTb4b4b4: Te6edf3FakeApiService
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3linksTb4b4b4: Te6edf3FakeDeviceLinkRepository
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3assetReaderTb4b4b4: Te6edf3FakeBundledAssetReader
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3repositoryTb4b4b4: Te6edf3DeviceHardwareRepositoryImpl

Tf0883e@BeforeTest
Tff7b72fun Td2a8ffsetupTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3databaseProvider Tff7b72= Te6edf3FakeDatabaseProviderTb4b4b4(Tb4b4b4)
Te6edf3api Tff7b72= Te6edf3FakeApiServiceTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3knownHardwareTb4b4b4)Tb4b4b4)
Te6edf3links Tff7b72= Te6edf3FakeDeviceLinkRepositoryTb4b4b4(Tb4b4b4)
Te6edf3assetReader Tff7b72= Te6edf3FakeBundledAssetReaderTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3knownHardwareTb4b4b4)Tb4b4b4, Te6edf3jsonTb4b4b4)
Te6edf3repository Tff7b72=
Te6edf3DeviceHardwareRepositoryImplTb4b4b4(
Te6edf3remoteDataSource Tff7b72= Te6edf3DeviceHardwareRemoteDataSourceTb4b4b4(Te6edf3apiTb4b4b4, Te6edf3dispatchersTb4b4b4)Tb4b4b4,
Te6edf3localDataSource Tff7b72= Te6edf3DeviceHardwareLocalDataSourceTb4b4b4(Te6edf3databaseProviderTb4b4b4)Tb4b4b4,
Te6edf3assetReader Tff7b72= Te6edf3assetReaderTb4b4b4,
Te6edf3json Tff7b72= Te6edf3jsonTb4b4b4,
Te6edf3deviceLinkRepository Tff7b72= Te6edf3linksTb4b4b4,
Te6edf3bootloaderOtaQuirksRepository Tff7b72= Te6edf3FakeBootloaderOtaQuirksRepositoryTb4b4b4(Te6edf3assetReaderTb4b4b4, Te6edf3jsonTb4b4b4)Tb4b4b4,
Te6edf3dispatchers Tff7b72= Te6edf3dispatchersTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@AfterTest Tff7b72fun Td2a8fftearDownTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3databaseProviderTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)

Tf0883e@Test
Tff7b72fun Td2a8ffrepeatedMissingModelUsesOneSuccessfulCatalogRefreshTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
T8b949e// Sequential lookups can hit cache after the first fetch and never share a flight; prove single-flight
T8b949e// sharing by suspending the fake API response and overlapping the two lookups.
Te6edf3apiTb4b4b4.Te6edf3responseGate Tff7b72= Te6edf3CompletableDeferredTb4b4b4(Tb4b4b4)
Te6edf3coroutineScope Tb4b4b4{
Tff7b72val Te6edf3first Tff7b72= Te6edf3async Tb4b4b4{ Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3second Tff7b72= Te6edf3async Tb4b4b4{ Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4) Tb4b4b4}
Te6edf3apiTb4b4b4.Te6edf3responseGateTff7b72!!Tb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3awaitAllTb4b4b4(Te6edf3firstTb4b4b4, Te6edf3secondTb4b4b4)
Tb4b4b4}

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3apiTb4b4b4.Te6edf3hardwareCallsTb4b4b4, Ta5d6ff"Ta5d6ffconcurrent callers must share one fetchTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3linksTb4b4b4.Te6edf3reconcileCallsTb4b4b4, Ta5d6ff"Ta5d6ffconcurrent callers must share one reconcileTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffreconciliationFailureDoesNotTriggerAnotherCatalogRefreshTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
T8b949e// The refresher records catalog success BEFORE calling reconcile(), so a reconcile failure must not
T8b949e// poison the success TTL window. A subsequent missing-model lookup must NOT cause another hardware
T8b949e// fetch, because the catalog was already recorded as fresh.
Te6edf3linksTb4b4b4.Te6edf3reconcileFailure Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffreconcile boomTa5d6ff"Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3linksTb4b4b4.Te6edf3reconcileFailure Tff7b72= Tff7b72null
Te6edf3assertNullTb4b4b4(Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3apiTb4b4b4.Te6edf3hardwareCallsTb4b4b4, Ta5d6ff"Ta5d6ffcatalog success must gate further hardware fetchesTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3linksTb4b4b4.Te6edf3reconcileCallsTb4b4b4, Ta5d6ff"Ta5d6ffretry outside TTL must not retry reconcile eitherTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffforceRefreshBypassesRecentSuccessfulCatalogRefreshTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff3T79c0ff7Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3apiTb4b4b4.Te6edf3hardwareCallsTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3linksTb4b4b4.Te6edf3reconcileCallsTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffemptyForcedRefreshPreservesPreviouslyCachedRemoteCatalogTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Te6edf3apiTb4b4b4.Te6edf3response Tff7b72= Te6edf3listOfTb4b4b4(Te6edf3remoteOnlyHardwareTb4b4b4)
Tff7b72val Te6edf3initial Tff7b72= Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= Te6edf3remoteOnlyHardwareTb4b4b4.Te6edf3hwModelTb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3remoteOnlyHardwareTb4b4b4.Te6edf3displayNameTb4b4b4, Te6edf3initialTff7b72?.Te6edf3displayNameTb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= Te6edf3knownHardwareTb4b4b4.Te6edf3hwModelTb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)Tb4b4b4)

Te6edf3apiTb4b4b4.Te6edf3response Tff7b72= Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3afterEmptyRefresh Tff7b72=
Te6edf3repositoryTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= Te6edf3remoteOnlyHardwareTb4b4b4.Te6edf3hwModelTb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Te6edf3remoteOnlyHardwareTb4b4b4.Te6edf3displayNameTb4b4b4, Te6edf3afterEmptyRefreshTff7b72?.Te6edf3displayNameTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3apiTb4b4b4.Te6edf3hardwareCallsTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3linksTb4b4b4.Te6edf3reconcileCallsTb4b4b4)
Tb4b4b4}

T8b949e// ── SoftDevice variant resolution: fail-closed on every unresolvable case ──────────────────────

Tff7b72private Tff7b72val Te6edf3nrfHardware Tff7b72=
Te6edf3NetworkDeviceHardwareTb4b4b4(
Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4,
Te6edf3hwModelSlug Tff7b72= Ta5d6ff"Ta5d6ffRAK4631Ta5d6ff"Tb4b4b4,
Te6edf3platformioTarget Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4,
Te6edf3architecture Tff7b72= Ta5d6ff"Ta5d6ffnrf52840Ta5d6ff"Tb4b4b4,
Te6edf3activelySupported Tff7b72= Tff7b72trueTb4b4b4,
Te6edf3displayName Tff7b72= Ta5d6ff"Ta5d6ffRAK4631Ta5d6ff"Tb4b4b4,
Te6edf3images Tff7b72= Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffrak4631.svgTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72fun Td2a8ffquirksAssetTb4b4b4(Te6edf3hwModelTb4b4b4: Tffa657Int Tff7b72= T79c0ff9Tb4b4b4, Te6edf3targetsTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6ff[Ta5d6ff\"Ta5d6ffrak4631Ta5d6ff\"Ta5d6ff]Ta5d6ff"Tb4b4b4, Te6edf3softDeviceTb4b4b4: Tffa657String? Tff7b72= Ta5d6ff"Ta5d6ff6.1.1Ta5d6ff"Tb4b4b4) Tff7b72=
Ta5d6ff"""
Ta5d6ff {
Ta5d6ff"Ta5d6ffdevicesTa5d6ff"Ta5d6ff: [],
Ta5d6ff"Ta5d6ffsoftDeviceVariantsTa5d6ff"Ta5d6ff: [
{ Ta5d6ff"Ta5d6ffhwModelTa5d6ff"Ta5d6ff: Tffd700$Te6edf3hwModelTa5d6ff, Ta5d6ff"Ta5d6ffhwModelSlugTa5d6ff"Ta5d6ff: Ta5d6ff"Ta5d6ffRAK4631Ta5d6ff"Ta5d6ff, Ta5d6ff"Ta5d6ffplatformioTargetsTa5d6ff"Ta5d6ff: Tffd700$Te6edf3targets
Ta5d6ff Tffd700${Te6edf3softDeviceTff7b72?.Te6edf3let Tb4b4b4{ Ta5d6ff"Ta5d6ff, Ta5d6ff\"Ta5d6ffsoftDeviceTa5d6ff\"Ta5d6ff: Ta5d6ff\"Tffd700$Te6edf3itTa5d6ff\"Ta5d6ff" Tb4b4b4} Tff7b72?: Ta5d6ff"Ta5d6ff"Tffd700}Ta5d6ff }
]
}
Ta5d6ff"""
Tb4b4b4.Te6edf3trimIndentTb4b4b4(Tb4b4b4)

T8b949e/** Rebuilds the repository around an nRF fixture so SoftDevice resolution can be exercised. */
Tff7b72private Tff7b72fun Td2a8ffnrfRepositoryTb4b4b4(Te6edf3quirksTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Te6edf3DeviceHardwareRepositoryImpl Tb4b4b4{
Te6edf3api Tff7b72= Te6edf3FakeApiServiceTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3nrfHardwareTb4b4b4)Tb4b4b4)
Te6edf3assetReader Tff7b72= Te6edf3FakeBundledAssetReaderTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3nrfHardwareTb4b4b4)Tb4b4b4, Te6edf3jsonTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{ Te6edf3quirksJson Tff7b72= Te6edf3quirks Tb4b4b4}
Tff7b72return Te6edf3DeviceHardwareRepositoryImplTb4b4b4(
Te6edf3remoteDataSource Tff7b72= Te6edf3DeviceHardwareRemoteDataSourceTb4b4b4(Te6edf3apiTb4b4b4, Te6edf3dispatchersTb4b4b4)Tb4b4b4,
Te6edf3localDataSource Tff7b72= Te6edf3DeviceHardwareLocalDataSourceTb4b4b4(Te6edf3databaseProviderTb4b4b4)Tb4b4b4,
Te6edf3assetReader Tff7b72= Te6edf3assetReaderTb4b4b4,
Te6edf3json Tff7b72= Te6edf3jsonTb4b4b4,
Te6edf3deviceLinkRepository Tff7b72= Te6edf3linksTb4b4b4,
Te6edf3bootloaderOtaQuirksRepository Tff7b72= Te6edf3FakeBootloaderOtaQuirksRepositoryTb4b4b4(Te6edf3assetReaderTb4b4b4, Te6edf3jsonTb4b4b4)Tb4b4b4,
Te6edf3dispatchers Tff7b72= Te6edf3dispatchersTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceResolvesWhenModelAndTargetMatchTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirksAssetTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Te6edf3SoftDeviceVariantTb4b4b4.Te6edf3S140_6_1_1Tb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWhenTheAssetIsAbsentTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirks Tff7b72= Tff7b72nullTb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertNotNullTb4b4b4(Te6edf3hardwareTb4b4b4, Ta5d6ff"Ta5d6ffhardware lookup must still succeed β€” only the variant is unavailableTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTb4b4b4.Te6edf3softDeviceVariantTb4b4b4, Ta5d6ff"Ta5d6ffan absent asset must not resolve a variantTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWhenTheAssetIsMalformedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirks Tff7b72= Ta5d6ff"Ta5d6ff{ not json at allTa5d6ff"Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertNotNullTb4b4b4(Te6edf3hardwareTb4b4b4, Ta5d6ff"Ta5d6ffa malformed asset must not fail the hardware lookupTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTb4b4b4.Te6edf3softDeviceVariantTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWhenTheModelIsUnmappedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirksAssetTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9T79c0ff9T79c0ff9Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4, Ta5d6ff"Ta5d6ffan unmapped model must not borrow another rowTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWhenTheReportedTargetIsNotInTheRowTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
T8b949e// The dangerous case: hwModel matches but the device reports a build we have not verified. Borrowing the row's
T8b949e// variant here is exactly what would write an erase image into a SoftDevice.
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirksAssetTb4b4b4(Te6edf3targets Tff7b72= Ta5d6ff"Ta5d6ff[Ta5d6ff\"Ta5d6ffrak4631_some_other_buildTa5d6ff\"Ta5d6ff]Ta5d6ff"Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4, Ta5d6ff"Ta5d6ffa target mismatch must refuse, not fall backTa5d6ff"Tb4b4b4)
Tb4b4b4}

T8b949e/** Two rows for the same hwModel with different SoftDevices β€” the shape hwModel 94 actually has. */
Tff7b72private Tff7b72fun Td2a8ffambiguousQuirksAssetTb4b4b4(Tb4b4b4) Tff7b72=
Ta5d6ff"""
Ta5d6ff {
Ta5d6ff"Ta5d6ffdevicesTa5d6ff"Ta5d6ff: [],
Ta5d6ff"Ta5d6ffsoftDeviceVariantsTa5d6ff"Ta5d6ff: [
{ Ta5d6ff"Ta5d6ffhwModelTa5d6ff"Ta5d6ff: 9, Ta5d6ff"Ta5d6ffhwModelSlugTa5d6ff"Ta5d6ff: Ta5d6ff"Ta5d6ffRAK4631Ta5d6ff"Ta5d6ff, Ta5d6ff"Ta5d6ffplatformioTargetsTa5d6ff"Ta5d6ff: [Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Ta5d6ff], Ta5d6ff"Ta5d6ffsoftDeviceTa5d6ff"Ta5d6ff: Ta5d6ff"Ta5d6ff6.1.1Ta5d6ff"Ta5d6ff },
{ Ta5d6ff"Ta5d6ffhwModelTa5d6ff"Ta5d6ff: 9, Ta5d6ff"Ta5d6ffhwModelSlugTa5d6ff"Ta5d6ff: Ta5d6ff"Ta5d6ffRAK4631Ta5d6ff"Ta5d6ff, Ta5d6ff"Ta5d6ffplatformioTargetsTa5d6ff"Ta5d6ff: [Ta5d6ff"Ta5d6ffrak4631_v2Ta5d6ff"Ta5d6ff], Ta5d6ff"Ta5d6ffsoftDeviceTa5d6ff"Ta5d6ff: Ta5d6ff"Ta5d6ff7.3.0Ta5d6ff"Ta5d6ff }
]
}
Ta5d6ff"""
Tb4b4b4.Te6edf3trimIndentTb4b4b4(Tb4b4b4)

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceResolvesWithoutAReportedTargetWhenTheModelIsUnambiguousTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirksAssetTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Te6edf3SoftDeviceVariantTb4b4b4.Te6edf3S140_6_1_1Tb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWithoutAReportedTargetWhenTheModelHasMultipleVariantsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
T8b949e// Without a device-reported build target, platformioTarget may be disambiguate()'s arbitrary pick β€” it
T8b949e// must not choose between rows whose SoftDevices differ.
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3ambiguousQuirksAssetTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4, Ta5d6ff"Ta5d6ffan ambiguous model must not resolve from a guessed targetTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceTreatsABlankReportedTargetAsAbsentTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3ambiguousQuirksAssetTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsoftDeviceIsNullWhenTheValueIsUnrecognisedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runBlocking Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3nrfRepositoryTb4b4b4(Te6edf3quirksAssetTb4b4b4(Te6edf3softDevice Tff7b72= Ta5d6ff"Ta5d6ff6.1.2Ta5d6ff"Tb4b4b4)Tb4b4b4)

Tff7b72val Te6edf3hardware Tff7b72= Te6edf3repoTb4b4b4.Te6edf3getDeviceHardwareByModelTb4b4b4(Te6edf3hwModel Tff7b72= T79c0ff9Tb4b4b4, Te6edf3target Tff7b72= Ta5d6ff"Ta5d6ffrak4631Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3hardwareTff7b72?.Te6edf3softDeviceVariantTb4b4b4, Ta5d6ff"Ta5d6ffan unknown SoftDevice string must not map to an imageTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72class T56d364DeviceHardwareRefreshGateTest Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3gate Tff7b72= Te6edf3DeviceHardwareRefreshGateTb4b4b4(Te6edf3retryIntervalMs Tff7b72= T79c0ff1T79c0ff0T79c0ff0Tb4b4b4, Te6edf3successTtlMs Tff7b72= T79c0ff1Te6edf3_000Tb4b4b4)

Tf0883e@Test
Tff7b72fun Td2a8fffreshCacheNeedsNoRefreshTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertFalseTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff0Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72falseTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fffailedAttemptIsThrottledUntilRetryIntervalTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertTrueTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff0Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Te6edf3gateTb4b4b4.Te6edf3recordAttemptTb4b4b4(T79c0ff0Tb4b4b4)

Te6edf3assertFalseTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff9T79c0ff9Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff1T79c0ff0T79c0ff0Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsuccessfulCatalogIsAuthoritativeForTtlTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3gateTb4b4b4.Te6edf3recordAttemptTb4b4b4(T79c0ff0Tb4b4b4)
Te6edf3gateTb4b4b4.Te6edf3recordSuccessTb4b4b4(T79c0ff1T79c0ff0Tb4b4b4)

Te6edf3assertFalseTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff1Te6edf3_009Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff1Te6edf3_010Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72falseTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffforceRefreshAlwaysBypassesGateTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3gateTb4b4b4.Te6edf3recordAttemptTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4)
Te6edf3gateTb4b4b4.Te6edf3recordSuccessTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3gateTb4b4b4.Te6edf3shouldRefreshTb4b4b4(Te6edf3nowMs Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3forceRefresh Tff7b72= Tff7b72trueTb4b4b4, Te6edf3cacheNeedsRefresh Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.12s